
import { getHostiles, getRoomDomain } from "../global/map";
import { getCreepCountCoeficient } from "../role/spawn";
import { UPGRADER_NORMAL_ENERGY_PERCENTAGE } from "../util/constants";
import { getActualFreeCapacity, getActualResourceAmount, getCapacity, getFreeCapacity, getResourceAmount, transportResource } from "../util/store";
import { jobGoto } from "./goto";

const predicates={
    _usable:            (structure)=>   (structure.my && structure.isActive()),
    
    _spawn:             (structure)=>   ((structure instanceof StructureSpawn)      && structure.my && structure.isActive()),
    empty_spawn:        (structure)=>   (predicates._spawn(structure) && getActualResourceAmount(structure) === 0),
    unfilled_spawn:     (structure)=>   (predicates._spawn(structure) && getActualFreeCapacity(structure)>0),
    extension:          (structure)=>   ((structure instanceof StructureExtension)  && structure.my && structure.isActive() && structure.store.getFreeCapacity(RESOURCE_ENERGY)>0),

    _tower:             (structure)=>   ((structure instanceof StructureTower)      && predicates._usable(structure)),
    _flourishing_or_emergent:(room)=>   (getCreepCountCoeficient(room)>=0.98 || getHostiles(room).some(((hostile)=>hostile.room.name === room.name))),
    inactive_tower:     (structure)=>   (predicates._tower(structure) && structure.store[RESOURCE_ENERGY]<10 && predicates._flourishing_or_emergent(Game.rooms[structure.room.memory.mainRoomName])),
    demanded_tower:     (structure)=>   (predicates._tower(structure) && structure.store[RESOURCE_ENERGY]/structure.store.getCapacity(RESOURCE_ENERGY)<0.65),
    unfilled_tower:     (structure)=>   (predicates._tower(structure) && structure.store.getFreeCapacity(RESOURCE_ENERGY)>0),

    empety_container:   (structure)=>   ((structure instanceof StructureContainer)  && structure.isActive() && getActualResourceAmount(structure) <= 50),
    storage:            (structure)=>   ((structure instanceof StructureStorage)    && structure.my && structure.isActive() && structure.store.getFreeCapacity(RESOURCE_ENERGY)>0),

    inactive_lab:       (structure)=>   ((structure instanceof StructureLab) && structure.isActive() && getActualResourceAmount(structure) === 0),
    demanded_lab:       (structure)=>   ((structure instanceof StructureLab) && structure.isActive() && getActualResourceAmount(structure)/getCapacity(structure)<0.8),
    unfilled_lab:       (structure)=>   ((structure instanceof StructureLab) && structure.isActive() && getActualFreeCapacity(structure)>0),

    _active_upgrader:   (creep)=>       ((creep instanceof Creep) && creep.memory.role==='upgrader' && creep.room.controller.pos.inRangeTo(creep, 7)),
    inactive_upgrader:  (creep)=>       (predicates._active_upgrader(creep) && ((getResourceAmount(creep)/getCapacity(creep) <= UPGRADER_NORMAL_ENERGY_PERCENTAGE && getCreepCountCoeficient(creep.room) > .80) || getResourceAmount(creep) === 0)),
    demanded_upgrader:  (creep)=>       (predicates._active_upgrader(creep) && getFreeCapacity(creep) > 0),
    unfilled_upgrader:  (creep)=>       (predicates._active_upgrader(creep) && getActualFreeCapacity(creep) > 0),

    inactive_maintainer:(creep)=>       ((creep instanceof Creep) && creep.memory.role==='maintainer' && creep.store[RESOURCE_ENERGY]===0),
    demanded_maintainer:(creep)=>       ((creep instanceof Creep) && creep.memory.role==='maintainer' && creep.store[RESOURCE_ENERGY]/creep.store.getCapacity()<0.8),
    unfilled_maintainer:(creep)=>       ((creep instanceof Creep) && creep.memory.role==='maintainer' && creep.store.getFreeCapacity()>0),
};
const priorityMap=[
    [predicates.unfilled_spawn, predicates.extension, predicates.inactive_upgrader, predicates.inactive_tower,predicates.inactive_maintainer,],
    [predicates.demanded_upgrader,predicates.demanded_tower,predicates.demanded_maintainer, predicates.demanded_lab,],

    [predicates.storage,predicates.empety_container,],
    [predicates.unfilled_tower,predicates.unfilled_upgrader,predicates.unfilled_maintainer, predicates.unfilled_lab,]
];

export const jobTransfer={
    /**
     * 
     * @param {Room} mainRoom 
     * @param {((value:Structure|Creep)=>Boolean)[]} predicateList 
     * @returns {Array<Structure|Creep>}
     */
    findTargets(mainRoom,predicateList){
        let targets=[];
        for(let roomName of getRoomDomain(mainRoom)){
            const room = Game.rooms[roomName];
            if(!room){
                continue;
            }
            for(let predicate of predicateList){
                targets.push(...room.find(FIND_STRUCTURES,{filter:predicate}));
                targets.push(...room.find(FIND_MY_CREEPS,{filter:predicate}));
            }
        }
        return targets;
    },
    /**
     * 
     * @param {Room} mainRoom
     * @returns {(AnyStoreStructure|AnyCreep)[]}
     */
    getTargets(mainRoom){
        if(mainRoom.__transferTargets !== undefined){
            return mainRoom.__transferTargets;
        }
        let targets;
        for(let types of priorityMap){
            targets=this.findTargets(mainRoom, types);
            if(targets.length>0){
                break;
            }
        }
        return mainRoom.__transferTargets = targets;
    },
    /**
     * 
     * @param {Room} mainRoom 
     * @param {(AnyStoreStructure|AnyCreep)[]} targets 
     * @returns {(AnyStoreStructure|AnyCreep)[]}
     */
    setTargets(mainRoom, targets){
        return mainRoom.__transferTargets = targets;
    },
    isStoring(mainRoom){
        if(mainRoom.__transferIsStoring !== undefined){
            return mainRoom.__transferIsStoring;
        }
        return mainRoom.__transferIsStoring = !this.getTargets(mainRoom).some((target)=>{
            return !(predicates.storage(target) || 
                predicates.empety_container(target) ||
                predicates.demanded_tower(target)) && getActualResourceAmount(target)>0;
        });
    },

    /**
     * 
     * @param {Creep} creep 
     * @param {Structure|Structure[]} target 
     * @param {MoveToOpts} opts 
     * @returns {GroupActionReturnCode}
     */
    run(creep, opts=undefined, target=undefined, say = false){
        if(opts===undefined){
            opts={visualizePathStyle:{stroke:"#00aaaa"}};
        }
        let targets;
        if(target instanceof Array){
            targets=target;
            target=target[0];
        }else if(target instanceof Structure){
            targets=[target];
        }else if(target===undefined){
            targets=this.getTargets(Game.rooms[creep.room.memory.mainRoomName]);
            targets=jobGoto.sortTargetsByDistance(creep, targets);
            target = targets[0];
        }
        if(targets.length === 0){
            return ERR_NOT_FOUND;
        }
        const previousLength = targets.length;

        let targetI = undefined;
        let closeCount = 0;
        let ret = undefined;
        for(let i=0; i<targets.length; i++){
            if(targets[i].id === target.id){
                targetI = i;
                continue;
            }
            if(ret !== OK){
                ret = transportResource(creep, targets[i], RESOURCE_ENERGY, undefined, true);
                if(ret === OK){
                    targets=targets.slice();
                    targets.splice(i,1);
                    if(closeCount > 0) break;
                    i--;
                }
            }
            if(targets[i].pos.isNearTo(creep.pos)){
                closeCount++;
            }
        }
        if(ret !== OK){
            if(target === undefined){
                console.log(creep, target, targets[targetI], targets.length, previousLength);
            }
            // targetI = 0;////

            ret = transportResource(creep, targets[targetI]);
            // ret = transportResource(creep, target);
            if(ret === OK){
                targets = targets.slice();
                targets.splice(targetI,1);
            }
        }

        if(closeCount === 0 && targets.length>0){
        // if(targets.length>0){
            jobGoto.run(creep, targets, opts);
            if(say && target)
                creep.say("trsf:"+target.id);
        }
        return ret;
    },
}